%==========================================================================
%=  This file is part of the Sound Restoration Project
%=  (c) Copyright Industrial Mathematics Institute
%=                University of South Carolina, Department of Mathematics
%=  ALL RIGHTS RESERVED
%=
%=  Author: Borislav Karaivanov
%==========================================================================

%==========================================================================
% List of the files on which this procedure depends:
%
% calibrateSignalsSingleMask.m
% findBestFitToSignal.m
% fitReduceFitSignal.m
% Curve Fitting toolbox
% visualizeSoundCalibrationSingleMask.m
% createMissingDirs.m
% computeOverlapGuess.m
% visualizeOverlapGuess.m
% writeToLogFile.m
% computeSignalOverlaps.m
% findMinRelativeDifference.m
% checkSignalOverlapsAgainstImages.m
% readImageByIndex.m
% readImageFile.m
% readdpx.m
% Image processing toolbox
% combineSignals.m
% visualizeOverlaps.m
% write2DArrayToFile.m
%
%==========================================================================

%==========================================================================
% The function "extractSoundWithSoundOverlapsTest" takes a collection of sound
% signals of the same length, calibrates them with a raw (or smoothed)
% additive mask based on their averages, and combines them into a single
% sound signal accounting for their overlaps. The overlaps are computed by
% finding the best match between the adjacent calibrated sound signals.
% REMARK: If infinity is specified as a value of the parameter
% "numSignalsPerMask" (see below), then all signals are used to compute a
% single calibration mask that is then applied to all of them.
% INPUT: "allSignalsArr" is a 2D array whose columns hold the individual
% sound signals.
% "imageFileNamePrefix" is a string specifying the common prefix (including
% a full directory path) in the names of the image files from which the
% individual sound signals have been extracted.
% "fileIndArr" is a 1D array specify (as integers) the indices of the
% image files from which the individual sound signals have been extracted.
% "imageBasedMaskArr" is an optional 2D image array of the same size as the
% image files to be processed. It specifies a multiplicative calibration
% mask which is to be applied to the images before any other processing.
% "numSignalsPerMask" is an optional, positive, odd integer specifying the
% number of consecutive signals to be used in the construction of each
% sound-based calibration mask. 
% "numSmoothings" is an optional non-negative integer specifying the number
% of smoothing passes to be applied to the raw averages of the signals.
% "movingAveSpan" is an optional, positive, odd integer specifying the span
% of the stencil to be used for moving average smoothing of the average
% signal.
% "degOfPolyFitArr" is an optional vector of non-negative integer
% specifying degrees of polynomial to be fitted to the average sound
% signal. The best fitting of these polynomials is to be sampled and used
% as a sound-based calibration mask. If this parameter is specified, then
% the values of the previous two parameters are ignored.
% "numToUseForGuess" is an optional, positive integer specifying the number
% of pairs of consecutive signals whose overlaps are to be computed. These
% pairs are to be chosen randomly among all available pairs of consecutive
% signals.
% "initSearchRad" is a positive integer specifying the radius of an
% interval centered at the overlap guess where the initial search for the
% best-matching overlap is carried out. It is to reflect the belief of the
% calling function with respect to how far the best-matching overlaps are
% likely to be from the overlap guess.
% "locMinNbhdRad" is an optional, positive integer specifying the radius of
% the neighborhood (an interval, in fact, in 1D) around a point to be
% checked in order to claim a local minimum at that point. More precisely,
% we consider having a local minimum of the relative differences at a fixed
% overlap value if for all overlaps in the neighborhood of that value the
% relative differences are not smaller.
% "indFieldLength" is an optional parameter specifying the number of digits
% used to encode the index of an image in its file name. Image indices are
% specified with leading zeros. For example, if the index field length is
% 6, then the index of image 241 is specified as 000241 in its file name.
% "imageFileExt" is a string specifying the common file extension of the
% image files from which the individual sound signals have been extracted.
% "waitbarStruct" is an optional parameter specifying a structure of
% parameters used to set a waitbar. Those parameters are a waitbar handle,
% a starting position on the waitbar (a number between 0 and 1), a relative
% length along the waitbar (a number between 0 and (1 - starting position),
% an initial value for a counter that counts the events whose progress is
% being illustrated by the waitbar, and a total reflecting the total number
% of events of the said kind.
% OUTPUT: "signalArr" is column vector returning the single sound signal
% combining all given sound signals.
%==========================================================================
function signalArr = extractSoundWithSoundOverlapsTest(allSignalsArr, ...
    imageFileNamePrefix, fileIndArr, imageBasedMaskArr, numSignalsPerMask, ...
    numSmoothings, movingAveSpan, degOfPolyFitArr, numToUseForGuess, ...
    initSearchRad, locMinNbhdRad, indFieldLength, imageFileExt, waitbarStruct)

if (nargin < 3)
    fileIndArr = (0:(size(allSignalsArr, 2) - 1));
end
if (nargin < 4)
    imageBasedMaskArr = [];
end
if (nargin < 5)
    numSignalsPerMask = 101;
end
if (nargin < 6)
    numSmoothings = 1;
end
if (nargin < 7)
    movingAveSpan = 15;
end
if (nargin < 8)
    degOfPolyFitArr = 2;
end
if (nargin < 9)
    numToUseForGuess = 400;
end
if (nargin < 10)
    initSearchRad = 10;
end
if (nargin < 11)
    locMinNbhdRad = 5;
end
if (nargin < 12)
    indFieldLength = 6;
end
if (nargin < 13)
    imageFileExt = 'dpx';
end
% Initialize the output. Otherwise, in case of an early "return" (due to an
% interactive request to cancel) we will get a complaint that an output
% argument has not been assigned during the call to this function.
signalArr = [];
% Ensure that the number of signals contributing to the calibration mask do
% not exceed the number of all available signals.
numSignalsPerMask = min(numSignalsPerMask, size(allSignalsArr, 2));

lsj = 'extract 1'

%=========================================================================%
%========== Here is the essential portion of this function. ==============%
%=========================================================================%
% Calibrate the signals.
maxAllowedCut = 150;

lsj_allSignalsArr = allSignalsArr(1,1)
lsj_numSmoothings = numSmoothings
lsj_movingAveSpan = movingAveSpan
lsj_degOfPolyFitArr = degOfPolyFitArr
lsj_maxAllowedCut = maxAllowedCut

[calibSignalsArr, soundBasedMaskArr, leftCut, rightCut] = ...
    calibrateSignalsSingleMask(allSignalsArr, numSmoothings, ...
    movingAveSpan, degOfPolyFitArr, maxAllowedCut);

% step = 500;
% for k = 1:step:size(calibSignalsArr, 2)
%     figure;
%     plot(mean(calibSignalsArr(:, k:(min(k + step - 1, size(calibSignalsArr, 2)))), 2));
%     xlabel([num2str(k) '-' num2str(k + step - 1)]);
% end


% Determine the most popular amount of overlap between any two consecutive
% signals.
overlapGuess = computeOverlapGuess(calibSignalsArr, numToUseForGuess, locMinNbhdRad);

lsj = 'extract 2'

% Determine the amount of overlap between every two consecutive signals.
%tic
[overlapArr, minDiffArr] = computeSignalOverlaps(calibSignalsArr, ...
    overlapGuess, initSearchRad, locMinNbhdRad, waitbarStruct(1));
%timeElapsed = toc;
%fprintf(1, 'new version finished in %f seconds.\n', timeElapsed);

if getappdata(waitbarStruct(1).handle,'canceling')
	return
end

lsj = 'extract 3'

% Reevaluate the amount of overlap between every two consecutive signals
% when deemed necessary using the appropriate images adjusted with a
% sound-based calibration.
checkThreshold = -1;
%checkThreshold = 10;
[imgCorrOverlapArr, imgCorrMinDiffArr] = checkSignalOverlapsAgainstImages(calibSignalsArr, ...
    leftCut, rightCut, overlapArr, overlapGuess, checkThreshold, ...
    imageFileNamePrefix, fileIndArr, imageBasedMaskArr, ...
    repmat(soundBasedMaskArr, 1, size(calibSignalsArr, 2)), ...
    initSearchRad, locMinNbhdRad, indFieldLength, imageFileExt, waitbarStruct(2));

if getappdata(waitbarStruct(2).handle, 'canceling')
	return;
end

isZeroArr = (imgCorrMinDiffArr == 0);
imgCorrMinDiffArr(isZeroArr) = minDiffArr(isZeroArr);

% Combine the calibrated sound signals into a single sound signal.
signalArr = combineSignals(calibSignalsArr, imgCorrOverlapArr, waitbarStruct(3));
if getappdata(waitbarStruct(3).handle, 'canceling')
	return;
end


% Smooth the combined sound signal with a moving average filter.
soundMovingAveSpan = 5;
signalArr = smooth(signalArr, soundMovingAveSpan, 'moving');
%=========================================================================%

lsj = 'extract 4'

% if (isempty(overlapArr) == false)
%     % Choose 5 signals to be visualized.
%     signalToShowIndArr = randi(numel(overlapArr), min(5, numel(overlapArr)), 1);
%     % Visualize and save snapshots, if desired.
%     visualizeOverlaps(allSignalsArr, calibSignalsArr, leftCut, rightCut, ...
%         {'Snd', 'Snd_ImgCorr'}, {overlapArr, imgCorrOverlapArr}, ...
%         {minDiffArr, imgCorrMinDiffArr}, numSignalsPerMask, ...
%         numSmoothings, movingAveSpan, signalToShowIndArr);
% end

lsj = 'extract 5'

% Write info to a log file.
writeToLogFile(['maximal allowed reduction of signal: ' num2str(maxAllowedCut)]);
writeToLogFile(['number of smoothing passes: ' num2str(numSmoothings)]);
writeToLogFile(['moving average span for mask smoothing: ' num2str(movingAveSpan)]);
writeToLogFile(['number of signals per mask: ' num2str(numSignalsPerMask)]);
writeToLogFile(['moving average span for sound signal smoothing: ' num2str(soundMovingAveSpan)]);
writeToLogFile(['overlap guess: ' num2str(overlapGuess)]);
writeToLogFile(['number of random signals used in computing overlap guess: ' num2str(numToUseForGuess)]);
writeToLogFile(['neighborhood radius for local minimum testing: ' num2str(locMinNbhdRad)]);
writeToLogFile(['initial search radius for local minimum being global: ' num2str(initSearchRad)]);
writeToLogFile(['threshold for computing image-based overlap: ' num2str(checkThreshold)]);
return;
% end of the function "extractSoundWithSoundOverlapsTest"
